package ar.uba.fi.tonyvaliente.persistors;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.BitSet;

import ar.uba.fi.tonyvaliente.files.Persistor;
import ar.uba.fi.tonyvaliente.files.PersistorException;
import ar.uba.fi.tonyvaliente.utils.BitSetUtils;
import ar.uba.fi.tonyvaliente.utils.io.ByteCompletionMode;
import ar.uba.fi.tonyvaliente.utils.io.InputBitstream;
import ar.uba.fi.tonyvaliente.utils.io.OutputBitstream;

/**
 * Convierte objetos de clase <code>BitSet</code> en
 * arrays de bytes y viceversa.
 * @author dmorello
 */
public class BitSetPersistor extends Persistor<BitSet> {
	
	private static final int BUFFER_SIZE_IN_BITS = 64;
	private ByteCompletionMode bcmode;
	
	/**
	 * Crea una instancia de la clase que, a la hora
	 * de serializar o deserializar el bitset, considera
	 * el modo de relleno del ultimo byte especificado
	 * en el parametro.
	 * @param bcmode modo de relleno del ultimo byte
	 */
	public BitSetPersistor(ByteCompletionMode bcmode) {
		this.bcmode = bcmode;
	}

	/**
	 * Crea un <code>BitSet</code> a partir del array de bytes
	 * recibido por parametro, considerando que el ultimo byte
	 * fue rellenado segun el <code>ByteCompletionMode</code>
	 * recibido en el constructor.
	 * @param bytes un array de bytes correspondiente al
	 * <code>BitSet</code> serializado
	 * @return BitSet deserializado
	 * @throws PersistorException si ocurre un error al deserializar
	 */
	@Override
	public BitSet deserialize(byte[] bytes) throws PersistorException {
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
		BitSet bs = new BitSet(0);
		try {
			InputBitstream in = new InputBitstream(bais, bcmode);
			BitSet aux;
			int bitsRead;
			do {
				aux = new BitSet(BUFFER_SIZE_IN_BITS);
				if ((bitsRead = in.read(aux)) > 0) {
					bs = BitSetUtils.concat(bs, aux);
				}
			} while (bitsRead == aux.size());
			
			in.close();
		} catch (IOException e) {
			throw new PersistorException("Error al deserializar BitSet", e);
		}
		return bs;
	}

	@Override
	/**
	 * Convierte un <code>BitSet</code> en un array de bytes.
	 * El ultimo byte del array se rellena de acuerdo al
	 * <code>ByteCompletionMode</code> recibido por parametro
	 * en el constructor.
	 * @param t BitSet a serializar
	 * @return array de bytes con el BitSet serializado.
	 * @throws PersistorException si ocurre un error al serializar.
	 */
	public byte[] serialize(BitSet t) throws PersistorException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			OutputBitstream out = new OutputBitstream(baos, bcmode);
			out.write(t);
			out.close();
		} catch (IOException e) {
			throw new PersistorException("Error al serializar BitSet: ", e);
		}
		
		return baos.toByteArray();
	}

}
